home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Hot Mix 17
/
Hot Mix 17.iso
/
HM17_SGI
/
research
/
lib
/
map_set.pro
< prev
next >
Wrap
Text File
|
1997-07-08
|
39KB
|
981 lines
; $Id: map_set.pro,v 1.64 1997/04/08 21:29:26 dave Exp $
;
; Copyright (c) 1993-1997, Research Systems, Inc. All rights reserved.
; Unauthorized reproduction prohibited.
PRO map_struct_append, In_struct, Name, Value, SUPERCEDE = super
;
; Append/Replace the tagname Name, with a given value to the In_Struct.
; If SUPERCEDE is set, and a tag with the given Name already exists,
; replace its value.
; If SUPERCEDE is NOT set, and the given tag exists, do nothing.
;
ntags=N_TAGS(In_struct)
if ntags eq 0 then begin ;If In_struct is undef, just return the tag
In_struct = CREATE_STRUCT(name, value)
endif else begin
tnames=tag_names(IN_STRUCT)
index=where(tnames eq NAME, count)
if count eq 0 then $ ;No match, append
In_Struct = CREATE_STRUCT(in_struct, name, value) $
else if keyword_set(super) then $
in_struct.(index[0]) = value ;Overwrite value?
;Otherwise, tag is already there, don't add
endelse
end
PRO MAP_STRUCT_MERGE, orig, add, SUPERCEDE = super
; Add the keywords in the struct add to the original struct orig.
; ADD must be either undefined or defined as a structure.
; If a given tag name is present in both structures:
; If SUPERCEDE is set, then the value of add.tag replaces the
; original value. If SUPERCEDE is not set, the original value
; remains unchanged.
if n_elements(orig) eq 0 then begin
if n_elements(add) ne 0 then orig = add
return
endif
otag = tag_names(orig)
atag = tag_names(add)
for i=0, n_elements(atag)-1 do begin ;Add the new ones
match = where(otag eq atag[i], count)
if count eq 0 then $
orig = create_struct(orig, atag[i], add.(i)) $ ;Disjoint tag name, add it
else if keyword_set(super) then $ ;Got a match, if super is set, overwrite.
orig.(match[0]) = add.(i) ;Common, add if set
endfor
end
FUNCTION map_point_valid, lon, lat, u, v
; Return 1 if the point at geo-coordinates (lon, lat) is mappable,
; 0 if not.
; Mappable means that the point is transformable, and is not clipped
; by the mapping pipeline.
; Input: lon, lat = input coordinates in degrees.
; Output: u, v = uv coordinates if point is mappable. The contents of
; u and v are undefined if the function returns 0, indicating that the
; point is not mappable.
;
u = 0.0 & v = 0.0
p = convert_coord(lon, lat, /DATA, /TO_NORM) ;Cvt to normalized coords
if finite(p(0)) eq 0 then return, 0
u = (p(0) - !x.s(0)) / !x.s(1) ;To UV coords
v = (p(1) - !y.s(0)) / !y.s(1)
clat = cos(lat * !dtor) ;Cvt lon lat to cartesian 3D coords
xyz = [ clat * cos(lon * !dtor), clat * sin(lon * !dtor), sin(lat * !dtor)]
p = !map.pipeline
s = size(p)
for i=0, s(2)-1 do begin ;# of stages
case p(0,i) of
0 : return, 1 ;0 = END, means we're successful.
1 : dummy = 0 ;Ignore splits
2 : if total(xyz * p(1:3,i)) + p(4,i) lt 0 then $ ;Outside clip plane?
return, 0
3 : dummy = 0 ;Ignore transforms
4 : if u lt p(1,i) or v lt p(2,i) or $ ;Within UV bounding box?
u gt p(3,i) or v gt p(4,i) then return, 0
endcase
endfor
return, 1 ;Should never hit here.
end ;map_point_valid
PRO map_set_ll_box
;Try to determine a lon/lat range from the existing parameters, and
;save in !MAP.LL_BOX = [latmin, lonmin, latmax, lonmax].
; If we can't reliably determine the lat/lon limits then the
; coorresponding min and max are both set to zero.
; If either pole is mappable, then all longitudes are mappable
north = map_point_valid(0, 90)
south = map_point_valid(0, -90)
dateline = (north + south eq 0) and map_point_valid(-180., !map.p0lat) and $
(map_point_valid(0., !map.p0lat) eq 0) ; Dateline visible
if north and south then return ;The whole globe is visible
if north then begin
lonmax = 180. & lonmin = -180. ;All longitudes are visible
latmax = 90. & latmin = 90.
endif else if south then begin
lonmax = 180. & lonmin = -180.
latmax = -90. & latmin = -90.
endif else begin
if dateline then begin
lonmax = 0. & lonmin = 360.
endif else begin
lonmax = -180. & lonmin = 180.
endelse
latmin = 90. & latmax = -90.
endelse
box = !map.uv_box
; Edges are in order: bottom, right, top, left.
sidex = [0, 2, 2, 0 ,0] ;X index of UV corner
sidey = [1, 1, 3, 3, 1] ;Y index
uvcen = (box([0,1]) + box([2,3]))/2.
del = 8 ;# of intervals
found = bytarr(4)
; March along the 4 uv edges of the plot, keeping the range in
; geo-coordinates.
for iside = 0, 3 do begin
s0 = box([sidex(iside), sidey(iside)]) ;Start of edge
s1 = box([sidex(iside+1), sidey(iside+1)]) ;End of edge
for i = 0, del-1 do begin
s2 = s0 + (s1-s0) * (float(i)/del) ;UV coordinate
n2 = s2 * [!x.s(1), !y.s(1)] + [!x.s(0), !y.s(0)] ;Normalized coord
p = convert_coord(n2(0), n2(1), /NORMAL, /TO_DATA)
if finite(p(0)) then begin
if dateline and (p(0) lt 0) then p(0) = p(0) + 360
lonmax = lonmax > p(0)
lonmin = lonmin < p(0)
latmax = latmax > p(1)
latmin = latmin < p(1)
found(iside) = 1 ;Found a legit point
endif
endfor ;for i
endfor ;for iside
if total(found) eq 4 then begin ;If we didn't values along 4 sides then punt
!map.ll_box = [latmin, lonmin, latmax, lonmax]
; print, !map.ll_box, format='(4f10.2)'
endif
end
PRO map_set_limits, limit, uvrange
; Limit = 4 or 8 point vector containing lat/lon limits. (input).
; WARNING: limit may be changed.
; uvrange = uv extent of map in form of [umin, vmin, umax, vmax]
; (output). Also, on output, limit is converted to the 8 point form.
;
; Set unit scaling for convert_coord:
!x.s = [0,1]
!y.s = [0,1]
if n_elements(limit) eq 4 then begin ;cvt 4 pt to 8 pt limit.
; *********** limit = [latmin, lonmin, latmax, lonmax]
n = 7 ;Check an NxN grid within the lat/lon limits
londel = limit[3] - limit[1]
if londel lt 0 then londel = londel + 360
lats = findgen(n) * ((limit[2]-limit[0])/(n-1)) + limit[0]
lons = findgen(n) * (londel / (n-1)) + limit[1]
j = 0
for i=0, n^2-1 do begin ;Check the grid for extrema
uv = convert_coord(lons[i mod n], lats[i/n], /TO_NORM, /DATA)
if finite(uv[0]) then begin
if j ne 0 then begin ;Already got a limit?
uvrange[0] = uvrange[0] < uv[0]
uvrange[2] = uvrange[2] > uv[0]
uvrange[1] = uvrange[1] < uv[1]
uvrange[3] = uvrange[3] > uv[1]
endif else uvrange = uv[[0,1,0,1]]
j = j + 1
endif ;in range
endfor
if j lt 2 then message, 'At least two limit points must be mappable', /INFO
endif else if n_elements(limit) eq 8 then begin
; 8 point limit in form of:
; [latLeft,lonLeft, latTop, lonTop, LatRt, lonRt, LatBot, LonBot]
;
for i=0,3 do begin
uv = convert_coord(limit[i*2+1], limit[i*2], /TO_NORM, /DATA)
if finite(uv[0]) eq 0 then $
message, 'Unmappable limit point: ' + $
string(limit[i*2+1], limit[i*2]), /INFO
; Left = uvrange(0)=uv(0); Top = uvrange(3) = uv(1); Right =
; uvrange(2) = uv(0); Bot = uvrange(1) = uv(1)
uvrange[([0,3,2,1])[i]] = uv[([0,1,0,1])[i]]
endfor
endif else message, 'Map limit must have 4 or 8 points'
end
FUNCTION map_rotxyz, p, rx, ry, rz ;Rotate the vector p(3) counterclockwise
; about the X, Y, and Z, by the amounts rx, ry, rz, degrees, in order.
;
p1 = p
dtor = !dpi/ 180.
if rx ne 0.0 then begin
sx = sin(rx * dtor) & cx = cos(rx * dtor)
t = [[1,0,0],[0,cx, sx], [0, -sx, cx]]
p1 = t # p1
endif
if ry ne 0.0 then begin
sy = sin(ry * dtor) & cy = cos(ry * dtor)
t = [[cy, 0, -sy], [0, 1, 0], [sy, 0, cy]]
p1 = t # p1
endif
if rz ne 0.0 then begin
sz = sin(rz * dtor) & cz = cos(rz * dtor)
t = [[cz, sz, 0], [ -sz, cz, 0], [0,0,1]]
p1 = t # p1
endif
return, p1
end
FUNCTION GREAT_CIRCLE, lon0, lat0, lon1, lat1, RADIANS=radians, PRINT=print
; Return: [sin(c), cos(c), sin(az), cos(az)] from the great circle
; distance between two points. c is the great circle angular distance,
; and az is the azimuth between two points.
if KEYWORD_SET(radians) eq 0 then k = !dtor else k = 1.0
coslt1 = cos(k*lat1)
sinlt1 = sin(k*lat1)
coslt0 = cos(k*lat0)
sinlt0 = sin(k*lat0)
cosl0l1 = cos(k*(lon1-lon0))
sinl0l1 = sin(k*(lon1-lon0))
cosc = sinlt0 * sinlt1 + coslt0 * coslt1 * cosl0l1 ;Cos of angle between pnts
sinc = sqrt(1.0 - cosc^2)
if abs(sinc) gt 1.0e-6 then begin
cosaz = (coslt0 * sinlt1 - sinlt0*coslt1*cosl0l1) / sinc ;Azmuith
sinaz = sinl0l1*coslt1/sinc
endif else begin ;Its antipodal
cosaz = 1.0
sinaz = 0.0
endelse
if keyword_set(print) then begin
print, 'Great circle distance: ', acos(cosc) / k
print, 'Azimuth: ', atan(sinaz, cosaz)/k
; print,'sinaz, cosaz = ', sinaz, cosaz
endif
return, [sinc, cosc, sinaz, cosaz]
end
PRO map_satellite_limit, n, xr, yr
; Return n or less points, defining the limit of the limb of a satellite
; projection
a = findgen(n) * (2 * !pi / (n-1))
st = !map.p[0] > 1.0
st = sqrt((st-1.)/(st+1.)) ;Map limit radius for satellite projection
xr = cos(a) * st ;Taken from Snyder Pg. 175
yr = sin(a) * st
a = yr * !map.cosr + xr * !map.sinr
xr = xr * !map.cosr - yr * !map.sinr
yr = a
a = yr * !map.p[2]/(!map.p[0]-1.0) + !map.p[3]
good = where(a gt 0.0, count)
if count eq 0 then begin ;If nothing, return nothing
trash = temporary(xr) & trash = temporary(yr)
return
endif
if count lt n then begin ;Extract & shift so pnts on map are contiguous
i0 = 0 ;Get 1st good pnt following a bad pnt
for i=1,count-1 do if good[i-1]+1 ne good[i] then i0 = i
good = shift(good,-i0)
endif
a = a[good]
xr = xr[good] * !map.p[3] / a
yr = yr[good]/a
end
PRO map_proj_info, iproj, CURRENT=current, $
UVRANGE=r, WIDTH=meters, NAME=name, CYLINDRICAL=cyl, $
PROJ_NAMES = proj_names_p, CIRCLE=is_circular, $
AZIMUTHAL=is_azimuthal, UV_LIMITS = uv_limits, $
LL_LIMITS = ll_limits
; Return information about maps in general, or projection iproj.
; Iproj = projection number, Input. If CURRENT is set, then the
; current map projection is returned in Iproj.
; CURRENT = if set, use current proj number, and return that index in iproj.
; UVRANGE = UV limits of the given projection, [umin, vmin, umax, vmax],
; WIDTH = (output) the width, in meters, of the the map at a scale
; of 1:1. The radius of the earth is used. If this result is 0, the
; projection's UV coordinates are already in meters.
; NAME = the name of the projection.
; CYLINDRICAL = 1 if the projection is cylindrical or pseudo-cylindrical.
; PROJ_NAMES = string array containing the names of all the
; available projections.
; UV_LIMITS = returned rectangle of the current map in UV coordinates,
; [Umin, Vmin, Umax, Vmax].
; LL_LIMITS = returned rectangle of the current map in latitude/longitude
; coordinates, [Lonmin, Latmin, Lonmax, Latmax]. Note, the
; range may not always be available, and the min and max may
; both be set to 0.
; Issues for a new projection:
; UVrange, proj_scale, is_cyl, is_azimuthal, circle, name, horizon, clipping
;
common map_set_common, proj_scale, proj_is_cyl, proj_names, circle, $
proj_is_azim, user_defined
on_error, 2
if n_elements(proj_scale) eq 0 then begin ;Init constant arrays?
p2 = 2 * !pi
s8 = 2 * sqrt(8)
ro = 2 * 2.628
; St Or LC La Gn Az Sa Cy Me Mo Si Ai Ha Al Tm Mi Ro
; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
proj_scale = [ 0, 4, 2, 4, 4, 4, p2, 1, p2, p2, s8, p2, p2, s8, 4, 0, p2,ro]
proj_is_cyl = [ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1]
proj_is_azim= [ 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
circle = [ 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0]
proj_names = $
["", "Stereographic", "Orthographic", "LambertConic", "LambertAzimuthal", $
"Gnomic", "AzimuthalEquidistant", "Satellite", "Cylindrical", $
"Mercator", "Mollweide", "Sinusoidal", "Aitoff", "HammerAitoff", $
"AlbersEqualAreaConic", "TransverseMercator", $
"MillerCylindrical", "Robinson"]
user_defined = bytarr(n_elements(proj_names))
endif
max_proj = n_elements(proj_names)-1
if arg_present(proj_names_p) then proj_names_p = proj_names
if KEYWORD_SET(current) then iproj = !map.projection ;Get current projection
if ARG_PRESENT(uv_limits) then uv_limits = !map.uv_box
if ARG_PRESENT(ll_limits) then ll_limits = !map.ll_box
if n_elements(iproj) eq 0 then return ;Rest of params are proj specific
if iproj lt 1 or iproj gt max_proj then $
message, 'Projection number must be within range of 1 to'+ strtrim(max_proj,2)
ones = [-1., -1., 1., 1.]
r_earth = 6378206.4d0 ;Earth equatorial radius, meters, ; Clarke 1866 ellipsoid
cyl = proj_is_cyl[iproj]
name = proj_names[iproj]
meters = proj_scale[iproj] * r_earth
is_circular = circle[iproj]
if arg_present(r) then case iproj of
1: r = ones ;Stereo
2: r = ones ;Ortho
3: r = 2 * ones ;Lambert Conic
4: r = 2 * ones ;lambert
5: r = 2 * ones ;gnomic
6: r = !DPI * ones ;azimuth
7: if !map.p[1] ne 0 then begin ;Complicated satellite?
map_satellite_limit, 60, xr, yr
if n_elements(xr) eq 0 then r = [0,0,0,0] $
else r = [min(xr, max=maxxr), min(yr, max=maxyr), maxxr, maxyr]
meters = (r[2]-r[0]) * r_earth
endif else begin
st = !map.p[0] > 1.0 ;For satellite
r = sqrt((st-1)/(st+1)) * ones ;Simple case satellite
meters = (r[2]-r[0]) * r_earth
endelse
8: r = [-1., -.5, 1., .5] * !DPI ;Cylind equidistant
9: r = [-!DPI, -2.43, !DPI, 2.43] ;Mercator (+/- 80 degs of lat) ~ 98%
10: r = sqrt(2.0) * [-2.,-1.,2.,1.] ;Mollweide
11: r = [-1., -.5, 1., .5] * !DPI ;sinusoidal
12: r = [-1., -.5, 1., .5] * !DPI ;Aitoff
13: r = sqrt(2.0) * [-2., -1., 2., 1.] ; Hammer-aitoff
14: r = 2 * ones ;Albers Conic
15: r = 2*!pi * r_earth * ones ;Transverse Mercator, Units = meters.
16: r = [-!DPI, -1.832, !DPI, 1.832] ;Miller Cylind (+/- 80 degs of lat)
17: r = [-2.628, -1.349, 2.628, 1.349] ;Robinson
endcase
end
PRO map_horizon, FILL=fill, NVERTS=n, ZVALUE=zvalue, _EXTRA=Extra
; Draw/fill the horizon (limb) of a map
map_proj_info, /CURRENT, NAME=name, UVRANGE=p, CIRCLE=is_circular
r = !map.rotation ;Our rotation
;Can't do conics
if name eq "LambertConic" or name eq "AlbersEqualAreaConic" then return
if n_elements(n) le 0 then n = 60 ;# of vertices
a = findgen(n+1) * (2 * !pi / n) ;N angles from 0 to 2 pi
if name eq "Satellite" and !map.p[1] ne 0 then begin ;Satellite proj, tilted
map_satellite_limit, 60, xr, yr
r = 0.0 ;No more rotation
endif else if is_circular then begin ;Std circular projection
xr = (p[2]-p[0])/2. * cos(a)
yr = (p[3]-p[1])/2. * sin(a)
endif else if name eq "Sinusoidal" then begin ;Sinusoidal
; flon = (!map.out(3)-!map.out(2))/360.
flon = 1.0
xr = flon * !pi * cos(a)
yr = fltarr(n+1)
for i=0,n do case fix(a[i]/(!pi/2)) of
0: yr[i] = a[i]
1: yr[i] = !pi-a[i]
2: yr[i] = !pi-a[i]
3: yr[i] = a[i]-2*!pi
4: yr[i] = 0.0 ;Last pnt
endcase
endif else if name eq "Robinson" then begin ;Robinson
lp = [2.628, 2.625, 2.616, 2.602, 2.582, 2.557, 2.532, 2.478, 2.422, $
2.356, 2.281, 2.195, 2.099, 1.997, 1.889, 1.769, 1.633, 1.504, 1.399]
lm = [0., 0.084, 0.167, 0.251, 0.334, 0.418, 0.502, 0.586, 0.669, 0.752, $
0.833, 0.913, 0.991, 1.066, 1.138, 1.206, 1.267, 1.317, 1.349]
xr = [lp, -reverse(lp), -lp, reverse(lp)]
yr = [lm, reverse(lm), -lm, -reverse(lm)]
endif else if name eq "UserDefined" then begin ;User projection
endif else begin ;Rectangular
xr = [p[0], p[2], p[2], p[0], p[0]]
yr = [p[1], p[1], p[3], p[3], p[1]]
endelse
if r ne 0.0 then begin
t1 = xr * !map.cosr + yr * !map.sinr ;Now rotate by - angle...
yr = yr * !map.cosr - xr * !map.sinr
xr = t1
endif
xtsave = !x.type
if n_elements(zvalue) eq 0 THEN zvalue = 0
!x.type=0 ;Plot in UV space
if keyword_set(fill) then polyfill, xr, yr, _EXTRA=Extra, NOCLIP=0 $
else plots, xr, yr, zvalue, _EXTRA=Extra, NOCLIP=0
!x.type=xtsave
end
PRO map_set_split, ADD=add, EQUATOR = equator
; Set the splitting hemisphere. If EQUATOR is set, set the splitting
; point as if the center of projection was on the equator, i.e. use
; a latitude of 0.
pole = !map.pole[4:6] ;Locn of pole
if keyword_set(equator) then begin ;use lat = 0
split = [!map.p0lon, 0, -sin(!map.u0), cos(!map.u0), 0.0, 0.0]
endif else begin
coslat = cos(!map.v0)
p0 = [ cos(!map.u0) * coslat, sin(!map.u0) * coslat, sin(!map.v0)]
pln = crossp(p0, pole)
split=[!map.p0lon, !map.p0lat, pln, 0.0]
endelse
noadd = keyword_set(add) eq 0 ;= 0 if we're adding
MAP_CLIP_SET, RESET=noadd, SPLIT=split, TRANSFORM=noadd
end
PRO map_set_clip, pname
; Set up the default clipping/splitting for the given projection.
;
r = -1.0 ;Great circle clipping radius...
if pname eq "LambertConic" or pname eq "AlbersEqualAreaConic" then begin
isign = 2*(!map.p[3] ge 0.0)-1 ;Assume its in one hemisphere
; map_set_split, /EQUATOR
map_set_split
map_clip_set, CLIP_PLANE=[0,0,isign,0]
map_clip_set, CLIP_PLANE=[0,0,- isign, .95] ;Towards pole
endif else if pname eq 'Mercator' or pname eq 'MillerCylindrical' then begin
rm = 0.975 ;Go out 97.5% of the way to the poles
map_set_split
map_clip_set, CLIP_PLANE=[-!map.pole[4:6], rm]
map_clip_set, CLIP_PLANE=[!map.pole[4:6], rm]
endif $ ;Other cylindricals
else if pname eq "Stereographic" or pname eq "Orthographic" then r = -1.0e-5 $
else if pname eq "Satellite" then r = - 1.01/!map.p[0] $
else if pname eq "Gnomic" then r = -0.5 $
else if pname eq "LambertAzimuthal" or pname eq "AzimuthalEquidistant" then $
r=0.8 $
else if pname eq "TransverseMercator" then r = -0.4 $ ;UTM, a fudge factor.
else begin ;If we haven't setup a clip, add splitting
map_proj_info, /CURRENT, CYLINDRICAL=is_cyl
if is_cyl then map_set_split
endelse
; If r is set, set clipping to points on a plane whose normal
; passes thru the center (u0,v0), and at a distance of r from the
; origin. r < 0 is the side towards (u0,v0).
if r ne -1.0 then $
map_clip_set, CLIP_PLANE=[cos(!map.u0) * !map.coso, $
sin(!map.u0) * !map.coso, !map.sino, r]
map_clip_set, /TRANSFORM
end
; *****************************************************************
PRO MAP_SET, p0lat, p0lon, rot, $
; ********** Projection keywords:
PROJECTION=proj, $ ;The projection index
NAME=name, $ ;The projection name as a string
STEREOGRAPHIC = stereographic, $
ORTHOGRAPHIC = orthographic, $
CONIC = conic, $
LAMBERT = lambert, $
GNOMIC = gnomic, $
AZIMUTHAL = azimuth, $
SATELLITE = satellite, $ ;Also called general perspective proj
CYLINDRICAL = cylindrical, $
MERCATOR = mercator, $
MILLER_CYLINDRICAL=miller, $
MOLLWEIDE = mollweide, $
SINUSOIDAL = sinusoidal, $
AITOFF = aitoff, $ ;Original Aitoff projection
HAMMER = hammer, $ ;This is really the Hammer-Aitoff projection
ALBERS = albers, $ ;Alber's equal-area conic
TRANSVERSE_MERCATOR = utm, $ ;Transverse mercator for an ellipsoid
ROBINSON = robinson, $ ;Robinson projection
; Also called Gauss-Krueger in Europe.
; **** Projection specific keywords:
ELLIPSOID = ellips, $ ;Defines the ellipsoid for Transverse mercator.
;3 elements: a = equatorial radius (meters), e^2
;= eccentricity squared, k0 = scale on
;central meridian. e^2 = 2*f-f^2,
;where f = 1-b/a, b = polar radius
;Default is the Clarke 1866 ellipsoid, =
; [6378206.4d0, 0.00676866d0, 0.9996d0]
CENTRAL_AZIMUTH=cent_azim, $ ;Angle of central azimuth (degrees) for:
; Cylindrical, Mercator, Miller,
; Mollw, Robinson, and Sinusoidal
; projections. Default = 0.
; The pole is placed at an azimuth of
; CENTRAL_AZIMUTH degrees CCW of north.
STANDARD_PARALLELS = std_p, $ ;One or two standard parallels for conics,
; One or two element array.
SAT_P = Sat_p, $ ;Satellite parameters: Altitude expressed in
; units of radii, Omega, and rotation.
; Rotation may also specified by the
; rot parameter.
; ********** MAP_SET specific keywords:
CLIP=clip, $ ;Default = do map specific clipping,
; CLIP=0 to disable
SCALE=scale, $ ;Construct isotropic map with a given scale.
; Map Scale is 1:scale, otherwise fit to window
ISOTROPIC = iso, $, ;If set, make X and Y scales equal
LIMIT = limit, $ ;4 or 8 point lat/lon limit:
; 4 point: [latmin, lonmin, latmax, lonmax]
; 8 point: [latLeft,lonLeft, latTop,
; lonTop, LatRt, lonRt, LatBot, LonBot]
; ********** MAP_SET graphics keywords:
NOERASE=noerase, TITLE=title,$
ADVANCE = advance, COLOR=color, POSITION = position, $
NOBORDER=noborder, T3D=t3d, ZVALUE=zvalue, $
CHARSIZE = charsize, XMARGIN=xmargin, YMARGIN=ymargin, $
; ********** MAP_HORIZON keywords:
HORIZON=horizon, E_HORIZON=ehorizon, $ ; E_HORIZON = structure containing
; extra keywords passed to the
; map_horizon procedure, e.g.
; E_HORIZON={FILL:1}
; ********** MAP_CONTINENTS keywords:
CONTINENTS = continents, E_CONTINENTS=econt, $ ;E_CONTINENTS = structure
; containing extra keywords to be
; passed to
; map_continents, e.g. E_CONTINENTS={FILL:1}
USA=usa, HIRES = hires, $
MLINESTYLE=mlinestyle, MLINETHICK=mlinethick, CON_COLOR=con_color, $
; ********** MAP_GRID keywords:
GRID=grid, E_GRID=egrid, $ ;E_GRID = extra keywords structure
GLINESTYLE=glinestyle, GLINETHICK=glinethick, $
LABEL=label, LATALIGN=latalign, LATDEL=latdel, LATLAB=latlab, $
LONALIGN=lonalign, LONDEL=londel, LONLAB=lonlab, $
; Ignored, but here for compatibility:
WHOLE_MAP=whole_map
; *****************************************************************
;+
; Limit = A four or eight element vector.
; If a four element vector, [Latmin, Lonmin, Latmax,
; Lonmax] specifying the boundaries of the region
; to be mapped. (Latmin, Lonmin) and (Latmax,
; Lonmax) are the latitudes and longitudes of
; two diagonal points on the boundary with
; Latmin < Latmax and Lonmin < Lonmax.
; For maps that cross the international dateline,
; specify west longitudes as angles between
; 180 and 360.
; If an eight element vector: [ lat0, lon0,
; lat1, lon1, lat2, lon2, lat3, lon3] specify
; four points on the map which give, respectively,
; the location of a point on the left edge,
; top edge, right edge, and bottom edge of
; the map extent.
;-
; If the map range can be simply expressed as [latmin, lonmin, latmax,
; lonmax], then they are contained here. With many maps, this is
; impossible. These values are used to speed MAP_GRID and
; MAP_CONTINENTS, by not drawing elements that are clearly off the
; map.
common map_set_common
on_error, 2 ;Return to caller if error.
del = 1.0e-6
dtor = !dpi/ 180.
dpi2 = !dpi/2.
!map.ll_box = 0 ;Assume no limits
if n_elements(proj) then iproj = proj $
else if n_elements(name) then begin ;Name supplied, match it
map_proj_info, PROJ_NAMES = names ;Available names
name1 = strcompress(strupcase(name), /REMOVE_ALL) ;Ignore case & blanks
iproj = (where(name1 eq strupcase(names)))(0) ;Match it
if iproj lt 1 then message, 'Projection "'+name+'" not available'
endif else if keyword_set(stereographic) then iproj =1 $
else if keyword_set(orthographic) then iproj =2 $
else if keyword_set(conic ) then iproj =3 $
else if keyword_set(lambert) then iproj =4 $
else if keyword_set(gnomic ) then iproj =5 $
else if keyword_set(azimuth) then iproj =6 $
else if keyword_set(satellite) then iproj =7 $
else if keyword_set(mercator) then iproj =9 $
else if keyword_set(mollweide) then iproj = 10 $
else if keyword_set(sinusoidal) then iproj = 11 $
else if keyword_set(aitoff) then iproj = 12 $
else if keyword_set(hammer) then iproj = 13 $
else if keyword_set(albers) then iproj = 14 $
else if keyword_set(utm) then iproj = 15 $
else if keyword_set(miller) then iproj = 16 $
else if keyword_set(robinson) then iproj = 17 $
else iproj=8 ;Assume cylindrical
!map.projection = iproj
map_proj_info, /CURRENT, NAME=pname ;Get symbolic name
; Initial erase?
if !P.multi[0] eq 0 and keyword_set(advance) then erase
if (not KEYWORD_SET(noerase)) and (not KEYWORD_SET(advance)) THEN erase
; ******** Supply defaults **********
if n_params() lt 3 then rot = 0.0d0
if n_params() lt 2 then p0lon = 0d0
if n_params() lt 1 then p0lat = 0d0
if n_elements(cent_azim) le 0 then cent_azim = 0.0
if n_elements(color) eq 0 then color = !p.color ;Default color
if n_elements(title) eq 0 then title = " "
if n_elements(t3d) le 0 then t3d = 0
if n_elements(zvalue) eq 0 then zvalue = 0.
if n_elements(charsize) eq 0 then charsize = !p.charsize
if charsize le 0.0 then charsize = 1.0
if n_elements(clip) eq 0 then clip = 1 ;default for CLIP is ON
;Hack for CONIC backwards compatibility
if pname eq "LambertConic" and (rot ne 0) and n_elements(std_p) eq 0 then begin
std_p = [p0lat, p0lon] ;Two standard parallels
p0lon1 = rot ;and rotation is the longitude
endif else p0lon1 = p0lon
if abs(p0lat) gt 90.0 then $
message,'Latitude must be in range of +/- 90 degrees'
if abs(p0lon) gt 360.0 then $
message,'Longitude must be in range of +/- 360 degrees'
u = double(p0lon1) ;Reduce lon to +/- 180
while u lt -180.0 do u = u + 360.
while u gt 180.0 do u = u - 360.
!map.p0lon = u
!map.p0lat = p0lat
!map.u0 = u * dtor
!map.v0 = p0lat * dtor
!x.type = 3 ;New map code
!y.type = 0
; Simple projection? (used for (psuedo) cylindricals)
!map.simple = abs(p0lat) le del and abs(cent_azim) le del
!map.sino = sin(!map.v0)
!map.coso = cos(!map.v0)
!map.rotation = rot
!map.sinr = sin(rot * dtor)
!map.cosr = cos(rot * dtor)
; Compute position of Pole which is a distance of !PI/2 from
; (p0lon, p0lat), at an azimuth of rot CCW of north.
;
pole = [0.0, sin(cent_azim * dtor), cos(cent_azim * dtor)]
p2 = map_rotxyz(pole, 0, -p0lat, u) ;Rotate to put origin at (0,1,0)
plat = asin(p2[2])
cosla = sqrt(1.0-p2[2]^2)
if cosla eq 0.0 then begin ;On pole?
plon = 0.0 & sinln = 0.0 & cosln = 1.0
endif else begin
plon = atan(p2[1], p2[0])
sinln = p2[1] / cosla & cosln = p2[0] / cosla
endelse
; lon/lat, sin(lat), cos(lat), xyz, Location of pole
!map.pole = [plon, plat, p2[2], cosla, p2]
if pname eq "LambertConic" or pname eq "AlbersEqualAreaConic" then begin
if n_elements(std_p) eq 0 then begin
if p0lat ne 0 then s = [p0lat, p0lat] else s = [20., 20.]
endif else if n_elements(std_p) eq 1 then s = std_p[[0,0]] $
else if n_elements(std_p) eq 2 then s = std_p $
else message, 'STANDARD_PARALLELS must have 1 or 2 elements'
s = s * dtor
if pname eq "LambertConic" then begin
if ABS(!map.p0lat) eq 90. then $
message, 'Center of projection may not be pole for conics'
if s[0] * s[1] lt 0 then $
message, 'Standard parallels must be in same hemisphere'
if s[0] eq s[1] then n = sin(s[0]) $
else n = alog(cos(s[0])/cos(s[1])) / $
alog(tan(!dpi/4+s[1]/2)/tan(!dpi/4+s[0]/2))
if n eq 0.0 then message, $
'Equator is illegal standard parallel for conic'
F = cos(s[0]) * tan(!dpi/4 + s[0]/2)^n/n
r0 = F/tan(!dpi/4+!map.v0/2.)^n
!map.p = [n, F, r0, s]
endif else begin ;Albers conic
n = (sin(s[0]) + sin(s[1]))/2.
if n eq 0 then begin
message,'Standard parallels should not be 0', /INFO
n = 0.5
endif
c = cos(s[0])^2 + 2 * n * sin(s[0])
r0 = sqrt(c - 2*n*sin(!map.v0))/n
!map.p = [n, c, r0, s]
endelse
endif
if pname eq "Satellite" then begin ;Special params for satellite projection.
if n_elements(sat_p) eq 0 then !map.p[0] = 2.0 $ ;Altitude in radii
else !map.p[0] = sat_p[0]
if n_elements(sat_p) le 1 then omega = 0d0 else omega = dtor * sat_p[1]
;Save em. sat(1) = TRUE for simple case (Vertical perspective)
!map.p[1] = omega
!map.p[2] = sin(omega) ;Somega = p[1]
!map.p[3] = cos(omega) ;comega
if n_elements(sat_p) ge 3 then begin ; For backwards compatibility, if
; Sat_p contains three elements, interpret the 3rd element as the rotation:
!map.rotation = sat_p[2]
!map.sinr = sin(sat_p[2] * dtor)
!map.cosr = cos(sat_p[2] * dtor)
endif ;3 elements
endif
map_proj_info, /CURRENT, WIDTH=meters, UVRANGE=uvrange, $
CYLINDRICAL = is_cyl
; Set up cylindrical projections
if pname eq "TransverseMercator" then begin ;Special params for UTM
if n_elements(ellips) ne 3 then $ ;Default == Clarke 1866 ellipsoid
ellips = [6378206.4d0, 0.00676866d0, 0.9996d0]
; ellips is a 3 element array containing the ellipsoid parameters:
; [a, e^2, k0]
; a = Equatorial radius, in meters.
; b = polar radius, b = a * (1-f), f = 1-b/a
; e^2 = eccentricity^2 = 2*f-f^2, where f = flattening.
; k0 = scale on central meridian, = 0.9996 for UTM.
e_2 = ellips[1]/(1.0-ellips[1])
; k0 e2 e_2 a b m0 m
!map.p = [ellips[2], ellips[1], e_2, ellips[0], ellips[1], 0., 0.]
!x.s = [0,1] & !y.s = [0,1] & !x.type = 3 ;Fake coordinate system.
q = convert_coord(u, p0lat, /data, /to_norm) ;Get m0
!map.p[5] = !map.p[6] ;Set m0 from Center of projection
endif else if is_cyl then begin ;other Cylindrical or pseudo-cylind proj?
; I don't know why we can't solve for this angle (the azimuth of (u0,v0)
; from (xp, yp)) using the law of sines. This is solving it the hard
; and long way..... But it works....
az = atan(!map.coso * sin(!map.u0-plon), $
cosla * !map.sino - p2[2] * !map.coso *cos(!map.u0-plon))
!map.p[0] = dpi2-az
; print, 'Pole: ', plon/dtor, plat /dtor, ', Az=',az / dtor
endif
uvrange_orig = uvrange ;Save original UV
if is_cyl then begin ;Adjust for rotation & wrap in cylind projs
; Set the x coordinate of the edge in UV space for wrap-around detection.
; This is only revelant in (pseudo) cylindrical projections.
u = uvrange[[0,0,2,2]] ;The 4 corners.
v = uvrange[[1,3,1,3]]
t1 = u * !map.cosr + v * !map.sinr ;Now rotate by - angle...
v = v * !map.cosr - u * !map.sinr
uvrange[0] = min(t1, max=t2)
uvrange[2] = t2
uvrange[1] = min(v, max=t2)
uvrange[3] = t2
endif
if n_elements(limit) gt 0 then begin ;Get UV range.
if keyword_set(scale) then $
message, 'Conflicting keywords specified: LIMIT and SCALE', /INFO
lim = limit ;Save old
if n_elements(lim) eq 4 then !map.ll_box = lim ;Save if 4 point limit
map_set_limits, lim, uvrange
endif
; print,'Uvrange=', uvrange
;*****************************************************************
if keyword_set(position) then $
plot, [0,1], xsty=5, ysty=5, /nodata, $
title=title, /noerase, position=position, charsize=charsize $
else begin ;Position not set
; Use margins
if n_elements(xmargin) eq 1 then xmar = [xmargin, xmargin] $
else if n_elements(xmargin) eq 2 then xmar = xmargin $
else xmar = [1,1]
if n_elements(ymargin) eq 1 then ymar = [ymargin, ymargin] $
else if n_elements(ymargin) eq 2 then ymar = ymargin $
else ymar = [1,2]
plot, [0,1], XSTYLE=5, YSTYLE=5, /NODATA, COLOR=color, $
XMARGIN= xmar, YMARGIN= ymar, TITLE=title, /NOERASE, $
CHARSIZE = charsize
endelse
!x.type = 3 ;For new maps;
if keyword_set(noborder) eq 0 then begin
; fudge is used to add a bit of spacing between the border and the
; extent of the map region. fudge of 0.01 indicates that the extra
; spacing (internal map margin) should be 1% of the original map extent.
;
; this extra space is now turned off when the noborder keyword is set
;
fudge = 0.01
s = (uvrange[2]-uvrange[0]) * fudge
ss = (uvrange[3] - uvrange[1]) * fudge
uvrange = uvrange + [-s, -ss, s, ss]
endif
; Figure the size of the drawing area on the screen in normalized units
x_size = !x.window[1]-!x.window[0]
y_size = !y.window[1]-!y.window[0]
x_cen = total(!x.window)/2. ;Midpoints in X & Y
y_cen = total(!y.window)/2.
; Compute the X and Y scale factors, !x.s(1), !y.s(1):
if keyword_set(scale) then begin ;Absolute scale provided?
;Absolute size of entire map, meters / uvrange
if meters ne 0 then $ ;Anything with an inherent scale
s = (meters / scale) / (uvrange_orig[2]-uvrange_orig[0])$ ;meters/uv_unit
else s = 1.0 / scale ;projections already in meters
ss = !d.x_size / !d.x_px_cm / 100. ;width of window in meters
!x.s[1] = s / ss
!y.s[1] = !x.s[1] * !d.x_size / !d.y_size ;Adjust for aspect ratio
;Adjust the UV range to include the plotting window ****
t = [x_size / !x.s[1], y_size / !y.s[1]] / 2 ;half the UV range
c = [uvrange[0]+uvrange[2], uvrange[1]+uvrange[3]]/2 ;UV center
; Take smallest UV box:
uvrange = [c[0]-t[0] > uvrange[0], c[1]-t[1] > uvrange[1], $
c[0]+t[0] < uvrange[2], c[1]+t[1] < uvrange[3]] ;New uv range
endif else if keyword_set(iso) then begin
; Scale in pixels/uvunit, correct for ISOMETRIC aspect ratio. Use smaller
; of X and Y scale factors
sx = x_size * !d.x_size /(uvrange[2]-uvrange[0]) ;X scale
sy = y_size * !d.y_size/(uvrange[3]-uvrange[1]) ;Y scale
!x.s[1] = (sx < sy) / !d.x_size ;Set smaller
!y.s[1] = !x.s[1] * !d.x_size / !d.y_size
if sx gt sy then $ ;Resize window to fit map area
!x.window = x_cen + sy/sx/2 * [-x_size, x_size] $
else !y.window = y_cen + sx/sy/2 * [-y_size, y_size]
endif else begin ;Scale to fit WINDOW
!x.s[1] = x_size/(uvrange[2]-uvrange[0])
!y.s[1] = y_size/(uvrange[3]-uvrange[1])
endelse
; Compute offsets to center UV rectangle in the window
!x.s[0] = -(uvrange[0]+uvrange[2])/2. * !x.s[1] + x_cen
!y.s[0] = -(uvrange[1]+uvrange[3])/2. * !y.s[1] + y_cen
map_clip_set,/reset ;Clear clipping pipeline.
if keyword_set(clip) then begin ;Setup clipping
map_set_clip, pname ;Set default clipping?
if (n_elements(lim) gt 0) or keyword_set(scale) then $ ;Clip UV?
MAP_CLIP_SET, CLIP_UV = uvrange
; Set up plotting clip window
!p.clip([0,2]) = (uvrange([0,2]) * !x.s(1) + !x.s(0)) * !d.x_size
!p.clip([1,3]) = (uvrange([1,3]) * !y.s(1) + !y.s(0)) * !d.y_size
endif
!map.uv_box = uvrange ;Save UV range
if clip and total(abs(!map.ll_box)) eq 0 then $ ; Try to make a lon/lat range
map_set_ll_box
if keyword_set(noborder) eq 0 then $ ;Draw the border.
plots, !x.window[[0,1,1,0,0]], !y.window[[0,0,1,1,0]], $
COLOR=color, zvalue, /NORM, /NOCLIP, T3D=t3d
; Collect the common graphics keywords
map_struct_append, egraphics, "COLOR", color
map_struct_append, egraphics, "T3D", t3d
map_struct_append, egraphics, "ZVALUE", zvalue
; Process MAP_HORIZON keywords: **************
map_struct_merge, ehorizon, egraphics ;Add common graphics keywords
if keyword_set(horizon) then MAP_HORIZON, _EXTRA=ehorizon
; Process MAP_CONTINENT keywords: **************
if n_elements(mlinestyle) then map_struct_append, econt, "LINESTYLE", mlinestyle
if n_elements(mlinethick) then map_struct_append, econt, "THICK", mlinethick
if n_elements(con_color) then map_struct_append, econt, "COLOR", con_color
if n_elements(hires) then map_struct_append, econt, "HIRES", hires
if n_elements(continents) then map_struct_append, econt, "CONTINENTS", continents
if n_elements(usa) then map_struct_append, econt, "USA", usa
if n_elements(econt) gt 0 or keyword_set(continents) then begin
map_struct_merge, econt, egraphics ;Add common graphics kwrds
MAP_CONTINENTS, _EXTRA=econt
endif
; Process MAP_GRID keywords: **************
if n_elements(label) then map_struct_append, egrid, "LABEL", label
if n_elements(latlab) then map_struct_append, egrid, "LATLAB", latlab
if n_elements(lonlab) then map_struct_append, egrid, "LONLAB", lonlab
if n_elements(latdel) then map_struct_append, egrid, "LATDEL", latdel
if n_elements(londel) then map_struct_append, egrid, "LONDEL", londel
if n_elements(latalign) then map_struct_append, egrid, "LATALIGN", latalign
if n_elements(lonalign) then map_struct_append, egrid, "LONALIGN", lonalign
do_grid = (keyword_set(grid) + n_elements(egrid) + n_elements(glinestyle) + $
n_elements(glinethick)) ne 0
map_struct_merge, egrid, egraphics
if n_elements(glinestyle) then $ ;These keywords supersede those in egraphics
map_struct_append, egrid, "LINESTYLE", glinestyle
if n_elements(glinethick) then $
map_struct_append, egrid, "THICK", glinethick
if do_grid then MAP_GRID, CHARSIZE=charsize, _EXTRA=egrid
if KEYWORD_SET(ADVANCE) and !P.Multi[0] gt 0 THEN $
!P.Multi[0] = !P.Multi[0] - 1 $
else !p.multi[0] = !p.multi[1] * !p.multi[2] - 1
end